#ifndef MVACALCULATOR_H
#define MVACALCULATOR_H

//#define _test_

#include "TMath.h"
#include "TRandom.h"
#include "TVector3.h"
#include "TLorentzVector.h"
#include "TObjString.h"
#include "TString.h"
#include "TClass.h"
#include "TList.h"
#include "Sphericity.h"

#include "Math/VectorUtil.h"

#include <iostream>
#include <map>

#include <vector>
#include "TObjArray.h" /// we will use TObjArray.h instead of std::vector

class MVACalculator : public TNamed {

public:

MVACalculator ():TNamed("MVACalculator","MVACalculator"),_supportMethods(0),
var_sumEt(-1e10),var_Et1(-1e10),var_Et2(-1e10),var_Et3(-1e10),
var_Eta1(-1e10),var_Eta2(-1e10),var_Eta3(-1e10),
var_dphijet1jet2(-1e10),var_dphijet1jet3(-1e10),var_dphijet2jet3(-1e10),
var_detajet1jet2(-1e10),var_detajet2jet3(-1e10),var_detajet3jet4(-1e10),
var_sphericity_boost(-1e10),var_sphericity(-1e10),
var_dptjet1jet2(-1e10),var_dptjet1jet3(-1e10),var_djet1jet2pt(-1e10),
var_Et2byEt1(-1e10),var_Et3byEt1(-1e10),var_Et3byEt2(-1e10)
{

_supportMethods = new TList();

///_test_, remove the block  after testing

#ifdef _test_

var_Et1 = 40.687;
var_Et2 = 5.789;
var_Et3 = 3.4556;
var_sumEt=60;

_supportMethods->Add(new TObjString("sumEt"));
_supportMethods->Add(new TObjString("Et1"));
_supportMethods->Add(new TObjString("Et2"));
_supportMethods->Add(new TObjString("Et3"));
_supportMethods->Add(new TObjString("Et2ByEt1"));
_supportMethods->Add(new TObjString("Et3ByEt1"));
_supportMethods->Add(new TObjString("Et3ByEt2"));

#endif

////_test_
};


void ResetAllVariables()
{
var_sumEt=-1e10;
var_Et1=-1e10;var_Et2=-1e10;var_Et3=-1e10;
var_Eta1=-1e10;var_Eta2=-1e10;var_Eta3=-1e10;
var_dphijet1jet2=-1e10;var_dphijet1jet3=-1e10;var_dphijet2jet3=-1e10;
var_detajet1jet2=-1e10;var_detajet2jet3=-1e10;var_detajet3jet4=-1e10;
var_sphericity_boost=-1e10;var_sphericity=-1e10;
var_dptjet1jet2=-1e10;var_dptjet1jet3=-1e10;var_djet1jet2pt=-1e10;
var_Et2byEt1 = -1e10;
var_Et3byEt1=-1e10;
var_Et3byEt2=-1e10;

return;

}

void FillMap()
{

vals_["sumEt"] = sumEt();
vals_["Et1"] = Et1();
vals_["Et2"] = Et2();
vals_["Et3"] = Et3();
vals_["Et2byEt1"] = Et2byEt1();
vals_["Et3byEt1"] = Et3byEt1();
vals_["Et3byEt2"] = Et3byEt2();
vals_["Eta1"] = Eta1();
vals_["Eta2"] = Eta2();
vals_["Eta3"] = Eta3();
vals_["dphijet1jet2"] = dphijet1jet2();
vals_["dphijet1jet3"] = dphijet1jet3();
vals_["dphijet2jet3"] = dphijet2jet3();
vals_["detajet1jet2"] = detajet1jet2();
vals_["detajet2jet3"] = detajet2jet3();
vals_["detajet3jet4"] = detajet3jet4();
vals_["sphericity_boost"] = sphericity_boost();
vals_["sphericity"] = sphericity();
vals_["dptjet1jet2"] = dptjet1jet2();
vals_["dptjet1jet3"] = dptjet1jet3();
vals_["djet1jet2pt"] = djet1jet2pt();

}


///jets is  an array of TLorentzVectors

//MVACalculator (TString name, TString title,const std::vector<TLorentzVector> & jets):TNamed(name,title),
//MVACalculator (TString name, TString title):TNamed(name,title),

MVACalculator (TString name, TString title,TObjArray * & jets):TNamed(name,title),
_supportMethods(0),
var_sumEt(-1e10),var_Et1(-1e10),var_Et2(-1e10),var_Et3(-1e10),
var_Eta1(-1e10),var_Eta2(-1e10),var_Eta3(-1e10),
var_dphijet1jet2(-1e10),var_dphijet1jet3(-1e10),var_dphijet2jet3(-1e10),
var_detajet1jet2(-1e10),var_detajet2jet3(-1e10),var_detajet3jet4(-1e10),
var_sphericity_boost(-1e10),var_sphericity(-1e10),
var_dptjet1jet2(-1e10),var_dptjet1jet3(-1e10),var_djet1jet2pt(-1e10),
var_Et2byEt1(-1e10),var_Et3byEt1(-1e10),var_Et3byEt2(-1e10)
{

_supportMethods = new TList();
_supportMethods->Add(new TObjString("sumEt"));
_supportMethods->Add(new TObjString("Et1"));
_supportMethods->Add(new TObjString("Et2"));
_supportMethods->Add(new TObjString("Et3"));
_supportMethods->Add(new TObjString("Et2byEt1"));
_supportMethods->Add(new TObjString("Et3byEt1"));
_supportMethods->Add(new TObjString("Et3byEt2"));
_supportMethods->Add(new TObjString("Eta1"));
_supportMethods->Add(new TObjString("Eta2"));
_supportMethods->Add(new TObjString("Eta3"));
_supportMethods->Add(new TObjString("dphijet1jet2"));
_supportMethods->Add(new TObjString("dphijet1jet3"));
_supportMethods->Add(new TObjString("dphijet2jet3"));
_supportMethods->Add(new TObjString("detajet1jet2"));
_supportMethods->Add(new TObjString("detajet2jet3"));
_supportMethods->Add(new TObjString("detajet3jet4"));
_supportMethods->Add(new TObjString("sphericity_boost"));
_supportMethods->Add(new TObjString("sphericity"));
_supportMethods->Add(new TObjString("dptjet1jet2"));
_supportMethods->Add(new TObjString("dptjet1jet3"));
_supportMethods->Add(new TObjString("djet1jet2pt"));

FillMap();

/**
	Usage of _supportMethods

	double *res;
	MVACalculator * p = new MVACalculator();
	if (p->SupportedMethods()->FindObject("Et2")!=0) res = gROOT->ProcessLineFast(TString("p->")+"Et2()");
	cout<<*res<<endl;

**/


/**
ProcessEvent 
 --  Not supported now 

It's part of the cotr

}
void ProcessEvent(TObjArray * jets )
{

ResetAllVariables();

**/


if (!jets || jets->IsEmpty()) {cout<<"Please,provide me jets, return"<<endl; return;}


bool _isOk=true;

for (Int_t i = 0; i < jets->GetEntries(); ++i) 
if  (!TString( ((TClass *)jets->At(i)->IsA())->GetName() ).Contains("TLorentzVector")) { cout<<"We work only with TLorentzVector, return"<<endl; _isOk=false;};

if (!_isOk)  return;


	TLorentzVector _sumEt(0.,0.,0.,0.);

for (Int_t i = 0; i < jets->GetEntries(); ++i) {

/// sumEt()
	TLorentzVector * _jet = (TLorentzVector *) jets->At(i);
	_sumEt+=TLorentzVector(*_jet);


///Et1,Et2,Et3,Eta1,Eta2,Eta3
	if (i==0) {
			var_Et1 = ((TLorentzVector *) jets->At(i))->Et();
			var_Eta1 = ((TLorentzVector *) jets->At(i))->Eta();
		}
	if (i==1) 	{
			var_Et2 = ((TLorentzVector *) jets->At(i))->Et();
			var_Eta2 = ((TLorentzVector *) jets->At(i))->Eta();

		}
	if (i==2) {
			var_Et3 = ((TLorentzVector *) jets->At(i))->Et();
			var_Eta3 = ((TLorentzVector *) jets->At(i))->Eta();

		}
}

/// sumEt()
        var_sumEt = _sumEt.Et();

///sphericity, sphericity_boost
	Sphericity * spher = new Sphericity();
	spher->analyze(jets,2);

	var_sphericity=spher->sphericity();

//std::cout<<"var_sphericity = "<<var_sphericity<<std::endl;





TObjArray * _jets_boost= (TObjArray *) jets->Clone() ;


	for (Int_t i = 0; i < _jets_boost->GetEntries(); ++i)  {
		((TLorentzVector * ) _jets_boost->At(i))->Boost( -_sumEt.BoostVector() );
//	std::cout<<"Pt Jet  boosted # "<<i<<"  "<<  ((TLorentzVector * ) _jets_boost->At(i))->Et()<<std::endl;
}



 spher->analyze(_jets_boost,2);

  var_sphericity_boost=spher->sphericity();

//std::cout<<"var_sphericity_boost = "<<var_sphericity_boost<<std::endl;


///To clean the sphericity support
	_jets_boost->SetOwner(kTRUE); /// to remove duplicated objects
	delete _jets_boost;
	delete spher;

/// dptjet1jet2, dptjet1jet3, djet1jet2pt,detajet2jet3,detajet3jet4,detajet1jet2
///dphijet1jet2,dphijet1jet3, dphijet2jet3

//std::cout<<"jets->GetEntries() = "<<jets->GetEntries()<<std::endl;

	if ( jets->GetEntries()>1 ) {
			var_dptjet1jet2 = ((TLorentzVector *) jets->At(0))->Et() - ((TLorentzVector *) jets->At(1))->Et();

			TLorentzVector * _vec0 =(TLorentzVector *) jets->At(0);
			TLorentzVector * _vec1 =(TLorentzVector *) jets->At(1);

			TLorentzVector _diff = *_vec0 - *_vec1;

			var_djet1jet2pt = _diff.Et();
		
			var_detajet1jet2 = ((TLorentzVector *) jets->At(0))->Eta() - ((TLorentzVector *) jets->At(1))->Eta();

			var_dphijet1jet2 = ROOT::Math::VectorUtil::DeltaPhi<TLorentzVector,TLorentzVector>(*_vec0,*_vec1);

			}
	 if ( jets->GetEntries()>2 ) {
			TLorentzVector * _vec0 =(TLorentzVector *) jets->At(0);
			TLorentzVector * _vec1 =(TLorentzVector *) jets->At(1);
			TLorentzVector * _vec2 =(TLorentzVector *) jets->At(2);

			 var_dptjet1jet3 = ((TLorentzVector *) jets->At(0))->Et() - ((TLorentzVector *) jets->At(2))->Et();

			 var_detajet2jet3 = ((TLorentzVector *) jets->At(1))->Eta() - ((TLorentzVector *) jets->At(2))->Eta();

			var_dphijet1jet3 = ROOT::Math::VectorUtil::DeltaPhi<TLorentzVector,TLorentzVector>(*_vec0,*_vec2);

			var_dphijet2jet3 = ROOT::Math::VectorUtil::DeltaPhi<TLorentzVector,TLorentzVector>(*_vec1,*_vec2);


			}

	  if ( jets->GetEntries()>3 ) var_detajet3jet4 = ((TLorentzVector *) jets->At(2))->Eta() - ((TLorentzVector *) jets->At(3))->Eta();	


//std::cout<<" var_Et3 = " <<var_Et3<<std::endl;
//std::cout<<" var_Et2 = " <<var_Et2<<std::endl;
//std::cout<<"var_dptjet1jet3 = "<<var_dptjet1jet3<<std::endl;
//std::cout<<"var_dphijet1jet3 = "<<var_dphijet1jet3<<std::endl;
//std::cout<<"var_detajet2jet3 = "<<var_detajet2jet3<<std::endl;
//std::cout<<"var_detajet3jet4 = "<<var_detajet3jet4<<std::endl;

///var_Et1, var_Et2, var_Et3
///Et2byEt1, Et3byEt1, Et3byEt2

var_Et1 = (var_sumEt>0) ? var_Et1/var_sumEt : -1e10;
var_Et2 = (var_sumEt>0) ? var_Et2/var_sumEt : -1e10;
var_Et3 = (var_sumEt>0) ? var_Et3/var_sumEt : -1e10;
var_Et2byEt1 = (var_Et2>0 && var_Et1>0) ? var_Et2/var_Et1 : -1e10;
var_Et3byEt1 = (var_Et3>0 && var_Et1>0) ? var_Et3/var_Et1 : -1e10;
var_Et3byEt2 = (var_Et3>0 && var_Et2>0) ? var_Et3/var_Et2 : -1e10;

var_dptjet1jet2 = (var_dptjet1jet2>-1e10 && var_sumEt>0) ? var_dptjet1jet2/var_sumEt : -1e10;
var_dptjet1jet3 = (var_dptjet1jet3>-1e10 && var_sumEt>0) ? var_dptjet1jet3/var_sumEt : -1e10;
var_djet1jet2pt = (var_djet1jet2pt>-1e10 && var_sumEt>0) ? var_djet1jet2pt/var_sumEt : -1e10;
};

~MVACalculator() { if (_supportMethods!=0) { for (Int_t i=0;i<_supportMethods->GetEntries();i++) delete _supportMethods->At(i); delete _supportMethods; } };

TList * SupportedMethods () const { return _supportMethods;} 

double * sumEt() { return & var_sumEt; }
double * Et1() {  return & var_Et1;}
double * Et2() {  return & var_Et2;}
double * Et3() {  return & var_Et3;}

double * Et2byEt1() { return & var_Et2byEt1;}
double * Et3byEt1() { return & var_Et3byEt1;}
double * Et3byEt2() { return & var_Et3byEt2;}

double *  Eta1()  {return & var_Eta1;}
double *  Eta2()  {return & var_Eta2;}
double *  Eta3()  {return & var_Eta3;}

double * dphijet1jet2()  { return & var_dphijet1jet2;}
double * dphijet1jet3()  { return & var_dphijet1jet3;}
double * dphijet2jet3()  { return & var_dphijet2jet3;}


double * detajet1jet2()  { return & var_detajet1jet2; }
double * detajet2jet3()  { return & var_detajet2jet3; }
double * detajet3jet4()  { return & var_detajet3jet4; }


double * sphericity_boost() {return & var_sphericity_boost;}
double * sphericity() {return & var_sphericity;}

double * dptjet1jet2 ()   {  return & var_dptjet1jet2;} 
double * dptjet1jet3 ()   {  return & var_dptjet1jet3;} 
double * djet1jet2pt ()   {  return & var_djet1jet2pt;} 

double GetValue (TString methodName) { double * tmp = vals_[methodName.Data()]; return *tmp;}

private:

std::map<std::string, double *> vals_;


double var_Et2byEt1;
double var_Et3byEt1;
double var_Et3byEt2;



double var_dphijet1jet2;
double var_dphijet2jet3;
double var_dphijet1jet3;

double var_sphericity_boost;

double var_Et2;
double var_Et3;
double var_Eta1;
double var_Eta2;
double var_Eta3;
double var_dptjet1jet2;
double var_dptjet1jet3;
double var_djet1jet2pt;
double var_detajet1jet2;

double var_sphericity;
double var_aplanarity;


double var_sumEt;
double var_Et1;

double var_detajet2jet3;
double var_detajet3jet4;


double massalljets;

TList * _supportMethods;

public:
        ClassDef(MVACalculator,2)
};











#ifdef __MAKECINT__
ClassImp(MVACalculator)
#endif


#endif
